package com.atguigu.gmall.order.mq;


import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.feign.pay.PayFeignClient;
import com.atguigu.gmall.model.enums.OrderStatus;
import com.atguigu.gmall.mq.MqConst;
import com.atguigu.gmall.mq.RabbitConst;
import com.atguigu.gmall.mq.to.WareStockedRespTo;
import com.atguigu.gmall.order.service.OrderService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.units.qual.A;
import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;

/**
 * 随便开发业务的技巧：
 * 1、application.yaml 配置 spring.rabbit下的所有
 * 2、发送消息 自动注入 RabbitTemplate ，
 *      调用 RabbitTemplate.converAndSend(交换机，路由键)
 * 3、监听消息 @RabbitListener 标在方法上
 *      Message message, Channel channel,<?> 自己的数据类型
 *   手工确认模式
 *          try{
 *              业务
 *              channel.basicAck()
 *          }catch(e){
 *              channel.basicNack();
 *          }
 */
@Slf4j
@Service
public class OrderMessageConsumer {


    @Autowired
    OrderService orderService;

    @Autowired
    PayFeignClient payFeignClient;

    /**
     * 库存系统扣减完系统，无论是否成功，都会给queue.ware.order 发送消息
     * 交换机 MqConst.EXCHANGE_DIRECT_WARE_ORDER
     * 路由键 MqConst.ROUTING_WARE_ORDER
     * 队列 queue.ware.order
     * this.sendMessage(MqConst.EXCHANGE_DIRECT_WARE_ORDER, MqConst.ROUTING_WARE_ORDER, JSON.toJSONString(map));
     * @param message
     * @param channel
     * @param wareJson
     */
   // @RabbitListener(queues = "queue.ware.order")
    @RabbitListener(bindings ={
            @QueueBinding(
                    value = @Queue(value = MqConst.QUEUE_WARE_ORDER, durable = "ture",exclusive = "false",autoDelete = "false"),
                    exchange = @Exchange(value = MqConst.EXCHANGE_DIRECT_WARE_ORDER,durable = "ture",type = ExchangeTypes.TOPIC,autoDelete = "false"),
                    key = MqConst.ROUTING_WARE_ORDER
            )
    }) //监听@Queue 这个队列
    public void listenWareStock(Message message,Channel channel,String wareJson) throws IOException {
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        try {
            WareStockedRespTo wareStockedRespTo = new ObjectMapper().readValue(wareJson, WareStockedRespTo.class);
            //修改订单状态, 如果无需拆弹就是大订单id，如果需要拆弹就是子订单id
            String orderId = wareStockedRespTo.getOrderId();
            if ("DEDUCTED".equals(wareStockedRespTo.getStatus())){
                //库存扣减成功
                orderService.changeOrderStatus(Long.parseLong(orderId), OrderStatus.WAITING_DELEVER);

            }else {
                //库存扣减失败
                orderService.changeOrderStatus(Long.parseLong(orderId),OrderStatus.WATING_SCHEDULED);
                //补偿业务，给补货系统发送消息，通知其补货
                //更多的队列更多的联动



            }
            channel.basicAck(deliveryTag,false);
        } catch (JsonProcessingException e) {
            channel.basicNack(deliveryTag,false,true);
        }
    }


    /**
     * Message message,： 代表消息
     * Channel channel：消息的通道
     * 订单id   上次没处理完成的，MQ不会再把消息继续交给我们
     * @param message
     * @param channel
     * */
    @RabbitListener(queues = RabbitConst.ORDER_RELEASE_QUEUE)
    public void closeOrder(Message message, Channel channel,Long orderId) throws IOException {
        System.out.println("收到释放订单的消息："+orderId);
        //获取到mq给我们发来的消息的属性
        MessageProperties messageProperties = message.getMessageProperties();
        long deliveryTag = messageProperties.getDeliveryTag();

        //redelivered是true，说明这个消息是重新发来的
        Boolean redelivered = messageProperties.getRedelivered();
        System.out.println("redelivered："+redelivered);
        //关单处理
        //1、先去支付宝确认一下这个订单到底支付了没，如果没支付就进行关单
        try{
            //查询此交易是否交付
            orderService.closeOrder(orderId); //调无数遍
            System.out.println(orderId+"：关单完成");
            //幂等性。
//            if(redelivered){
//                log.info("由于系统原因，{}订单关单消息，重复投递进来",orderId);
//            }else {
//
//            }
            //业务正常处理。回复处理完成
            channel.basicAck(deliveryTag,false); //回复消息已经处理
        }catch (Exception e){
            //long deliveryTag, boolean multiple, boolean requeue
            //业务异常回复炸了
            channel.basicNack(deliveryTag,false,true); //我们失败了告诉mq，消息重新入队
        }finally {
            //long deliveryTag, boolean multiple
            //前面的业务执行完，如果网断了。
            /**
             * finally不是必须要执行的吗，
             * 为啥要把确认消息写到里面啊，
             * 能这样写是因为如果出现了异常catch里面已经把消息退回去了 ，
             * finall里面在确认收到就不会起到作用了吧
             */
        }

    }
}
